#pragma once

#include <Windows.h>
#include "Circle.h"
#include "ContactManifold.h"
#include "Line.h"
#include <vector>
#include "Rope.h"
#include "BodyInfo.h"
#include <concurrent_vector.h>

enum CmdType
{
	CMD_NEWCIRCLE,
	CMD_TOGGLESPRING,
	CMD_MANYCIRCLES
};

struct Command
{
	CmdType type;
	float	x;
	float	y;
	float	z;
};


class Game
{

public:
	Game();
	~Game(void);
	
	// Called from main() to update physics
	void					Update(float delta);

	// Saves a command received from the client 
	void					AddCommand(CmdType type, float x=0, float y=0, float z=0);

	// Activates/Deactivates the rope object
	void					toggleRope();
	
	// Pause/Unpause simulation
	void					pause();
	
	// Starts the simulation timer
	void					startTimer();

	// Returns the amount of time passed since the last call of this function
    double					getDeltaTime();
	
	// Returns the amount of time passed since the start of the timer
	double					getTime();

	// Get/Set
	Rope&					rope();
	bool					activeRope();
	int						getRopePointsCount();
	void*					getRopeData();

	void					setAirjet(Vector start, Vector end);
	float					getAirjetMagnitude();
	
	void					setElasticity(float v);
	void					setFriction(float v);
	float					getElasticity() const;
	float					getFriction() const;
	
	float					getAirjetForce() const;
	void					setAirjetForce(float v);
	
	int						getTotalObjects() const;
	float					physicsFreq();
	float					networkFreq();
	float					graphicsFreq();
	void					physicsFreq(float v);
	void					networkFreq(float v);
	void					graphicsFreq(float v);
	void					periodEnd(bool v);
	void*					ptrCircles();
	// Variables
	
	

private:
	// Processed any commands received during the last physics loop execution
	void					processCommands();
	
	// Updates the position of the lines representing the rope visually
	void					updateRopeVisuals();
	
	// Finds circles considered at rest and makes them inactive
	void					putInactiveAtRest();
	// Creates grid's lines
	void					createLines();
	
	// Adds a new circle in the simulation
	void					AddCircle(float x, float y, float mass);
	
	// Decides if a circle is inactive
	bool					atRest(Circle* circle);
	
	// Applies the air jet impulse to the circles
	void					ApplyImpulse();
	
	// Main physics function
	void					SimulationLoop(float delta);
	
	// Performs static and dynamic collision detection 
	void					CollisionDetection(float dt);
	
	// Static collision detection routine between two circles
	bool					StaticSphereCollisionDetection(Circle* circle1, Circle* circle2, ContactManifold* manifold);
	
	// Static collision detection routine between a circle and a line
	bool					StaticLineCollisionDetection(Line* line, Circle* circle, ContactManifold* manifold);
	
	// Sends detected collisions to collision response routines
	void					DynamicCollisionResponse(float dt);
	
	// Informs circles to integrate their velocities
	void					CalculateObjectPhysics();
	
	// Informs circles to integrate their positions
	void					UpdateObjectPhysics(float dt);
	
	// Checks for NaN values 
	void					checkData(std::string msg="");
	
	// Checks if a Vector has at least a NaN component
	bool					isNaN(Vector v);
	bool					isNaN(float x)  { if (x != x) return true; else return false; }


	// Vars
	float					m_physicsFreq;
	float					m_networkFreq;
	float					m_graphicsFreq;

	bool					bRope;
	Vector					mAirjetStart;
	Vector					mAirjetEnd;
	float					mAirjetMagnitude;
	bool					mAppliedAirJet;
	int						massId;
	__int64					start;
	double					freq;
	int						m_totalObjects;
	float					m_elasticity;
	float					m_friction;
	float					m_airjetForce;
	bool					mPause;
	bool					bPeriodEnd;
	float					m_dt;
	int						m_circleId;
	Rope					m_rope;
	ContactManifold*		m_manifold;
	std::vector<Line>		mLines;
	std::vector<Circle*>	m_circles;
	BodyInfo				m_circlesInfo[580];
	Concurrency::concurrent_vector<Command>	m_commands;
};

